#include "Palette.h"

Palette::Palette(int K,ColorBin *base_bin, int bin_size)
            :_K(K)
{
    Kmeans_init(base_bin,bin_size);
    K_means(base_bin,bin_size);
    Calc_Sigmar();
    Calc_Lambda();
}

Mat Palette::get_Lambda() const
{
    return _Lambda;
}

 ColorLabList Palette::get_orinPalette() const
 {
     return _oriPalette;
 }

 ColorLabList Palette::get_editPalette() const
 {
     return _editPalette;
 }

void Palette::Kmeans_init(ColorBin *base_bin,int bin_size)
{
	int bin_num = bin_size*bin_size*bin_size;
	float *bins_copy = new float[bin_num];
	int mmax = -1,mmax_ind;
	for (int i = 0 ; i < bin_num ; i++)
	{
		bins_copy[i] = (base_bin+i)->getNum();
		if(mmax<bins_copy[i])
		{
			mmax = bins_copy[i];
			mmax_ind = i;
		}
	}
	_oriPalette.push_back((base_bin+mmax_ind)->getCenter());
	for (int p = 0; p < _K-1; p++)
	{
		ColorLab tmp;
		float maxc = -1;
		for (int i = 0 ; i < bin_num ; i++)
		{
			if(bins_copy[i]==0)
				continue;
			else
			{
				float d2 = LabDistance2((base_bin+i)->getCenter(),_oriPalette[p]);
				float factor = 1 - exp(-d2 / 6400.0f); // sigma_a:80
				bins_copy[i] *= factor;
				if (bins_copy[i] > maxc)
				{
					maxc = bins_copy[i];
					tmp = (base_bin+i)->getCenter();
				}
			}
		}
		_oriPalette.push_back(tmp);
	}
	_oriPalette.push_back(RGB2Lab(8,8,8));
	delete bins_copy;
	return;
}

void Palette::K_means(ColorBin *base_bin,int bin_size)
{
	bool no_change = false;
	while (!no_change) {
		no_change = true;
        ColorLabList sum_color(_K+1);
		std::vector<int> sum_count(_K+1);
		for (int i = 0; i < _K + 1; i++) {
			sum_color[i] = ColorLab(0,0,0);
			sum_count[i] = 0;
		}
		for (int i = 0; i < bin_size; i++)
		{
			for (int j = 0; j < bin_size; j++)
			{
				for (int k = 0; k < bin_size; k++)
				{
					ColorBin *tmp_bin = base_bin+i*bin_size*bin_size+j*bin_size+k;
					if (tmp_bin->getNum() == 0)
					{
						continue;
					}

					ColorLab lab = tmp_bin->getCenter();
					float minD = 1e10;
					int minI = -1;
					for (int p = 0; p < _K + 1; p++)
					{
						float d = LabDistance2(_oriPalette[p], lab);
						if (minD > d) {
							minD = d;
							minI = p;
						}
					}
					if (minI != tmp_bin->getGroup())
					{
						tmp_bin->setGroup(minI);
						no_change = false;
					}
					ColorLab m = Weight_Mul(tmp_bin->getCenter(), tmp_bin->getNum());
					for(int ii = 0 ; ii < 3 ; ii++)
					{
						sum_color[minI][ii] += m[ii];
					}
					sum_count[minI] += tmp_bin->getNum();
				}
			}
		}
		for (int i = 0; i < _K + 1; i++)
		{
			if (sum_count[i])
			{
				for (int j = 0; j < 3; j++) {
					_oriPalette[i][j] = sum_color[i][j] / sum_count[i];
				}
			}
		}
	}
 	LabSort(_oriPalette);
 	for(int i = 0 ; i < _K+1 ; i++)
        _editPalette.push_back(_oriPalette[i]);
}

void Palette::Show_oriPalette(int Palette_size = 100) const
{
    Mat paletteMat(Palette_size,Palette_size*_K,CV_8UC3,Scalar(0));
    for(int i = 1 ; i < _oriPalette.size() ; i++)
	{
		ColorRGB res = Lab2RGB(_oriPalette[i]);
		//cout<<(int)res[0]<<" "<<(int)res[1]<<" "<<(int)res[2]<<endl;
		for(int r = 0 ; r<Palette_size ; r++)
		{
			for(int c = (i-1)*Palette_size ; c<(i)*Palette_size; c++)
			{
				paletteMat.at<Vec3b>(r,c)[0] = res[2];
				paletteMat.at<Vec3b>(r,c)[1] = res[1];
				paletteMat.at<Vec3b>(r,c)[2] = res[0];
			}
		}
	}
	imshow("image Palette",paletteMat);
}


void Palette::Show_editPalette(int Palette_size = 100)
{
    Mat paletteMat(Palette_size,Palette_size*_K,CV_8UC3,Scalar(0));
    for(int i = 1 ; i < _editPalette.size() ; i++)
	{
		ColorRGB res = Lab2RGB(_editPalette[i]);
		//cout<<(int)res[0]<<" "<<(int)res[1]<<" "<<(int)res[2]<<endl;
		for(int r = 0 ; r<Palette_size ; r++)
		{
			for(int c = (i-1)*Palette_size ; c<i*Palette_size; c++)
			{
				paletteMat.at<Vec3b>(r,c)[0] = res[2];
				paletteMat.at<Vec3b>(r,c)[1] = res[1];
				paletteMat.at<Vec3b>(r,c)[2] = res[0];
			}
		}
	}
	imshow("Edit Palette",paletteMat);
}


void Palette::EditPalette(int entry,const ColorLab& color)
{
    _editPalette[entry] = color;
    vector<ColorL> L_edit,L_origin;
    L_edit.push_back(0);
    L_origin.push_back(0);
    for(int i = 0 ; i < _K; i++)
    {
        L_edit.push_back(_editPalette[i+1][0]);
        L_origin.push_back(_oriPalette[i+1][0]);
    }
    L_edit.push_back(100);
    L_origin.push_back(100);
    for(int i = entry+1 ; i < L_origin.size() ; i++)
        L_edit[i]=max(L_origin[i],L_edit[i-1]);
    for(int i = entry-1 ; i >= 0; i--)
        L_edit[i]=min(L_origin[i],L_edit[i+1]);

    for(int i = 0 ; i < _K ; i++)
        _editPalette[i+1][0]=L_edit[i+1];
	return;
}

void Palette::Calc_Sigmar()
{
	float sum = 0;
	for (int i = 0 ; i < _K; i++)
    {
		for (int j = i+1 ; j < _K ; j++)
        {
			sum += sqrt(LabDistance2(_oriPalette[i+1], _oriPalette[j+1]));
		}
	}
	_Sigmar = sum/((_K-1)*_K);
	cout<<"_Sigmar = "<<_Sigmar<<endl;
	return;
}

void Palette::Calc_Lambda()
{
    Mat phi(_K,_K,CV_32FC1);
    float den = 2*_Sigmar*_Sigmar;
    for(int i = 0 ; i < _K ; i++)
    {
        for(int j = 0 ; j < _K ; j++)
        {
            phi.at<float>(i,j) = exp(-LabDistance2(_oriPalette[i+1],_oriPalette[j+1])/den);
        }
    }
    cv::invert(phi,_Lambda);
    return;
}

float Palette::get_Sigma() const
{
	return _Sigmar;
}

int Palette::get_K() const
{
	return _K;
}
